package org.apache.ibatis.common.logging.jdbc;

import org.apache.ibatis.common.logging.Log;
import org.apache.ibatis.common.reflection.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Collection;

/**
 * @Name ConnectionLogger
 * @Description: [JDBC Connection 对象代理（日志增强）] 实现类
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-05-15 17:09:34
 **/
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
    private final Connection connection;

    private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.connection = conn;
    }

    public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
        InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
        ClassLoader loader = Connection.class.getClassLoader();
        return (Connection) Proxy.newProxyInstance(loader, new Class[]{Connection.class}, handler);
    }

    public Connection getConnection() {
        return connection;
    }

    /**
     * 代理方法
     *
     * @param proxy  代理对象
     * @param method 目标方法
     * @param args   目标方法入参
     * @return java.lang.Object 目标方法执行结果
     * @version v1.0.0
     * @author lichao
     * @date 2024-05-15 17:12:50
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果方法继承自 Object 类，交由当前对象执行
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            // 拦截 Connection 中的 prepareStatement 方法
            if ("prepareStatement".equals(method.getName())) {
                // 打印日志
                if (isDebugEnabled()) {
                    debug(" Preparing: " + removeBreakingWhitespace((String) args[0]), true);
                }
                PreparedStatement ps = (PreparedStatement) method.invoke(connection, args);
                // 返回一个 PreparedStatement 的代理对象，该代理中加入了对 PreparedStatement 的日志打印操作
                ps = PreparedStatementLogger.newInstance(ps, statementLog, queryStack);
                return ps;
            }
            // 拦截 Connection 中的 prepareCall 方法
            else if ("prepareCall".equals(method.getName())) {
                // 打印日志
                if (isDebugEnabled()) {
                    debug(" Preparing: " + removeBreakingWhitespace((String) args[0]), true);
                }
                PreparedStatement ps = (PreparedStatement) method.invoke(connection, args);
                // 返回一个 PreparedStatement 的代理对象，该代理中加入了对 PreparedStatement 的日志打印操作
                ps = PreparedStatementLogger.newInstance(ps, statementLog, queryStack);
                return ps;
            }
            // 拦截 Connection 中的 createStatement 方法，不打印日志
            else if ("createStatement".equals(method.getName())) {
                PreparedStatement ps = (PreparedStatement) method.invoke(connection, args);
                // 返回一个 PreparedStatement 的代理对象，该代理中加入了对 PreparedStatement 的日志打印操作
                ps = PreparedStatementLogger.newInstance(ps, statementLog, queryStack);
                return ps;
            }
            // 其他方法，直接放行
            else {
                return method.invoke(connection, args);
            }
        } catch (Throwable e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }
}

